package com.ninong.ker.workflow.service.impl;

import cn.hutool.core.io.IoUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ninong.ker.admin.rest.vo.ActReDeploymentVO;
import com.ninong.ker.admin.service.ProcessDefinitionService;
import com.ninong.ker.admin.service.dto.DefinitionIdDTO;
import com.ninong.ker.admin.service.dto.ProcessDefXmlDTO;
import com.ninong.ker.admin.service.dto.ProcessDefinitionDTO;
import com.ninong.ker.workflow.mapper.ReDeploymentMapper;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntityImpl;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class ProcessDefinitionServiceImpl implements ProcessDefinitionService {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ReDeploymentMapper actReDeploymentMapper;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private HistoryService historyService;

    @Override
    public void insert(String stringBPMN) {
        DeploymentBuilder obj = repositoryService.createDeployment()
                .addString("CreateWithBPMNJS.bpmn", stringBPMN);
        obj.deploy();
    }

    @Override
    public Map<String, Object> listPage(ProcessDefinitionDTO dto) {
        List<ProcessDefinitionDTO> list = Lists.newArrayList();
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery().orderByProcessDefinitionId().orderByProcessDefinitionVersion().desc();
        if (StringUtils.isNotBlank(dto.getName())) {
            processDefinitionQuery.processDefinitionNameLike("%" + dto.getName() + "%");
        }
        if (StringUtils.isNotBlank(dto.getKey())) {
            processDefinitionQuery.processDefinitionKeyLike("%" + dto.getKey() + "%");
        }
        List<ProcessDefinition> processDefinitions = processDefinitionQuery.listPage((dto.getCurrentPage() - 1) * dto.getPageSize(), dto.getPageSize());
        long count = processDefinitionQuery.count();
        if (count != 0) {
            Set<String> ids = processDefinitions.parallelStream().map(pdl -> pdl.getDeploymentId()).collect(Collectors.toSet());
            List<ActReDeploymentVO> actReDeploymentVOS = actReDeploymentMapper.selectActReDeploymentByIds(ids);
            List<ProcessDefinitionDTO> processDefinitionDTOS = processDefinitions.stream()
                    .map(pd -> new ProcessDefinitionDTO((ProcessDefinitionEntityImpl) pd, actReDeploymentVOS.parallelStream().filter(ard -> pd.getDeploymentId().equals(ard.getId())).findAny().orElse(new ActReDeploymentVO())))
                    .collect(Collectors.toList());
            list.addAll(processDefinitionDTOS);
        }
        Map<String, Object> map = Maps.newHashMap();
        map.put("list", list);
        map.put("total", count);
        return map;
    }

    @Override
    public ProcessDefXmlDTO get(String id) {
        InputStream inputStream = repositoryService.getResourceAsStream(id, "CreateWithBPMNJS.bpmn");
        String xml = IoUtil.read(inputStream, "UTF-8");
        ProcessDefXmlDTO vo = new ProcessDefXmlDTO();
        vo.setStringBPMN(xml);
        return vo;
    }

    @Override
    public void delete(String[] ids) {
        List<String> idList = Arrays.asList(ids);
        idList.forEach(id -> {
            repositoryService.deleteDeployment(id, false);
        });
    }

    @Override
    public void updateState(ProcessDefinitionDTO dto) {
        // 当流程定义被挂起时，已经发起的该流程定义的流程实例不受影响（如果选择级联挂起则流程实例也会被挂起）。
        // 当流程定义被挂起时，无法发起新的该流程定义的流程实例。
        // 直观变化：act_re_procdef 的 SUSPENSION_STATE_ 为 2
        Integer state = dto.getSuspendState();
        if (state == 2) {
            repositoryService.suspendProcessDefinitionById(dto.getId());
        } else if (1 == state) {
            repositoryService.activateProcessDefinitionById(dto.getId());
        }
    }

    @Override
    public DefinitionIdDTO getDefinitionsByInstanceId(String instanceId) {
        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult();
        String deploymentId = processInstance.getDeploymentId();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        return new DefinitionIdDTO(processDefinition);
    }
}
